//
// Created by biu on 23-12-8.
//

#ifndef AGENT_GCOV_COVERAGE_TYPES_HPP
#define AGENT_GCOV_COVERAGE_TYPES_HPP

#include <nlohmann/json.hpp>

using json = nlohmann::json;

struct gcov_branch_ {
    bool fallthrough{};
    int count{};
    bool throw_{};
};
namespace nlohmann {
    template<>
    struct adl_serializer<gcov_branch_> {
        static void to_json(json &j, const gcov_branch_ &d) {
            j = json{{"fallthrough", d.fallthrough},
                     {"count",       d.count},
                     {"throw",       d.throw_}};
        }

        static void from_json(const json &j, gcov_branch_ &d) {
            if (j.contains("fallthrough") && j["fallthrough"].is_boolean()) {
                d.fallthrough = j["fallthrough"].get<bool>();
            } else {
                d.fallthrough = false;
            }
            if (j.contains("count") && j["count"].is_number_integer()) {
                d.count = j["count"].get<int>();
            } else {
                d.count = 0;
            }
            if (j.contains("throw") && j["throw"].is_boolean()) {
                d.throw_ = j["throw"].get<bool>();
            } else {
                d.throw_ = false;
            }
        }
    };
}
struct gcov_line_ {
    std::vector<gcov_branch_> branches{};
    int count{};
    int line_number{};
    bool unexecuted_block{};
    std::string function_name{};
};
namespace nlohmann {
    template<>
    struct adl_serializer<gcov_line_> {
        static void to_json(json &j, const gcov_line_ &d) {
            j = json{{"branches",         d.branches},
                     {"count",            d.count},
                     {"line_number",      d.line_number},
                     {"unexecuted_block", d.unexecuted_block},
                     {"function_name",    d.function_name}};
        }

        static void from_json(const json &j, gcov_line_ &d) {
            if (j.contains("branches") && j["branches"].is_array()) {
                d.branches = j["branches"].get<std::vector<gcov_branch_>>();
            } else {
                d.branches = {};
            }
            if (j.contains("count") && j["count"].is_number_integer()) {
                d.count = j["count"].get<int>();
            } else {
                d.count = 0;
            }
            if (j.contains("line_number") && j["line_number"].is_number_integer()) {
                d.line_number = j["line_number"].get<int>();
            } else {
                d.line_number = 0;
            }
            if (j.contains("unexecuted_block") && j["unexecuted_block"].is_boolean()) {
                d.unexecuted_block = j["unexecuted_block"].get<bool>();
            } else {
                d.unexecuted_block = false;
            }
            if (j.contains("function_name") && j["function_name"].is_string()) {
                d.function_name = j["function_name"].get<std::string>();
            } else {
                d.function_name = "";
            }
        }
    };
}
struct gcov_function_ {
    int blocks{};
    int end_column{};
    int start_line{};
    std::string name{};
    int blocks_executed{};
    int execution_count{};
    std::string demangled_name{};
    int start_column{};
    int end_line{};
};
namespace nlohmann {
    template<>
    struct adl_serializer<gcov_function_> {
        static void to_json(json &j, const gcov_function_ &d) {
            j = json{{"blocks",          d.blocks},
                     {"end_column",      d.end_column},
                     {"start_line",      d.start_line},
                     {"name",            d.name},
                     {"blocks_executed", d.blocks_executed},
                     {"execution_count", d.execution_count},
                     {"demangled_name",  d.demangled_name},
                     {"start_column",    d.start_column},
                     {"end_line",        d.end_line}};
        }

        static void from_json(const json &j, gcov_function_ &d) {
            if (j.contains("blocks") && j["blocks"].is_number_integer()) {
                d.blocks = j["blocks"].get<int>();
            } else {
                d.blocks = 0;
            }
            if (j.contains("end_column") && j["end_column"].is_number_integer()) {
                d.end_column = j["end_column"].get<int>();
            } else {
                d.end_column = 0;
            }
            if (j.contains("start_line") && j["start_line"].is_number_integer()) {
                d.start_line = j["start_line"].get<int>();
            } else {
                d.start_line = 0;
            }
            if (j.contains("name") && j["name"].is_string()) {
                d.name = j["name"].get<std::string>();
            } else {
                d.name = "";
            }
            if (j.contains("blocks_executed") && j["blocks_executed"].is_number_integer()) {
                d.blocks_executed = j["blocks_executed"].get<int>();
            } else {
                d.blocks_executed = 0;
            }
            if (j.contains("execution_count") && j["execution_count"].is_number_integer()) {
                d.execution_count = j["execution_count"].get<int>();
            } else {
                d.execution_count = 0;
            }
            if (j.contains("demangled_name") && j["demangled_name"].is_string()) {
                d.demangled_name = j["demangled_name"].get<std::string>();
            } else {
                d.demangled_name = "";
            }
            if (j.contains("start_column") && j["start_column"].is_number_integer()) {
                d.start_column = j["start_column"].get<int>();
            } else {
                d.start_column = 0;
            }
            if (j.contains("end_line") && j["end_line"].is_number_integer()) {
                d.end_line = j["end_line"].get<int>();
            } else {
                d.end_line = 0;
            }
        }
    };
}
struct gcov_file_ {
    std::vector<gcov_line_> lines{};
    std::vector<gcov_function_> functions{};
    std::string file{};
};
namespace nlohmann {
    template<>
    struct adl_serializer<gcov_file_> {
        static void to_json(json &j, const gcov_file_ &d) {
            j = json{{"lines",     d.lines},
                     {"functions", d.functions},
                     {"file",      d.file}};
        }

        static void from_json(const json &j, gcov_file_ &d) {
            if (j.contains("lines") && j["lines"].is_array()) {
                d.lines = j["lines"].get<std::vector<gcov_line_>>();
            } else {
                d.lines = {};
            }

            if (j.contains("functions") && j["functions"].is_array()) {
                d.functions = j["functions"].get<std::vector<gcov_function_>>();
            } else {
                d.functions = {};
            }

            if (j.contains("file") && j["file"].is_string()) {
                d.file = j["file"].get<std::string>();
            } else {
                d.file = "";
            }
        }
    };
}

struct gcov_coverage {
    std::string gcc_version{};
    std::vector<gcov_file_> files{};
    std::string format_version{};
    std::string current_working_directory{};
    std::string data_file{};
};

namespace nlohmann {
    template<>
    struct adl_serializer<gcov_coverage> {
        static void to_json(json &j, const gcov_coverage &d) {
            j = json{{"gcc_version",               d.gcc_version},
                     {"files",                     d.files},
                     {"format_version",            d.format_version},
                     {"current_working_directory", d.current_working_directory},
                     {"data_file",                 d.data_file}};
        }

        static void from_json(const json &j, gcov_coverage &d) {
            if (j.contains("gcc_version") && j["gcc_version"].is_string()) {
                d.gcc_version = j["gcc_version"].get<std::string>();
            } else {
                d.gcc_version = "";
            }
            if (j.contains("files") && j["files"].is_array()) {
                d.files = j["files"].get<std::vector<gcov_file_>>();
            } else {
                d.files = {};
            }
            if (j.contains("format_version") && j["format_version"].is_string()) {
                d.format_version = j["format_version"].get<std::string>();
            } else {
                d.format_version = "";
            }
            if (j.contains("current_working_directory") && j["current_working_directory"].is_string()) {
                d.current_working_directory = j["current_working_directory"].get<std::string>();
            } else {
                d.current_working_directory = "";
            }
            if (j.contains("data_file") && j["data_file"].is_string()) {
                d.data_file = j["data_file"].get<std::string>();
            } else {
                d.data_file = "";
            }
        }
    };
}


#endif //AGENT_GCOV_COVERAGE_TYPES_HPP
